package cn.zyq.ykb.service.admin.impl;

import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SecureUtil;
import cn.zyq.ykb.beans.admin.Admin;
import cn.zyq.ykb.beans.sys.SysAdmin;
import cn.zyq.ykb.mapper.admin.AdminMapper;
import cn.zyq.ykb.service.admin.AdminService;
import cn.zyq.ykb.system.annotation.OneClick;
import cn.zyq.ykb.system.common.BaseService;
import cn.zyq.ykb.system.common.PageParam;
import cn.zyq.ykb.system.config.consts.redis.RedisMgtKeys;
import cn.zyq.ykb.system.config.consts.system.RegularConst;
import cn.zyq.ykb.system.config.consts.system.SystemConfig;
import cn.zyq.ykb.system.config.redis.RedisService;
import cn.zyq.ykb.system.enums.core.DeletedTypeEnum;
import cn.zyq.ykb.system.enums.core.ResponseCodeEnum;
import cn.zyq.ykb.system.exception.BusinessException;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class AdminServiceImpl extends BaseService implements AdminService {

    @Autowired
    private RedisService redisService;
    @Autowired
    private AdminMapper adminMapper;
    //
    // @Autowired
    // Cache<String, Object> caffeineCache;
    /**
     * 创建新的token，并更新到缓存、数据库中
     *
     * @param admin
     * @return 返回新建的token
     */
    private String updateToken(Admin admin) {
        Admin storeInfo = this.getUpdateInstance(Admin.class, admin);
        storeInfo.setId(admin.getId());
        String token = this.uuid();
        storeInfo.setToken(token);
        admin.setToken(token);
        this.setToCache(admin);
        this.adminMapper.updateByPrimaryKeySelective(storeInfo);
        return token;
    }


    private Admin getByPhone(String phone) {
        Admin param = new Admin();
        param.setPhone(phone);
        param.setDeleted(DeletedTypeEnum.NORMAL.getValue());

        Admin dbInfo = this.adminMapper.selectOne(param);
        return dbInfo;
    }

    private Admin getByAccount(String account) {
        Admin param = new Admin();
        param.setAccount(account);
        param.setDeleted(DeletedTypeEnum.NORMAL.getValue());

        Admin dbInfo = this.adminMapper.selectOne(param);
        return dbInfo;
    }
    private void verifyPhone(String phone) {
        if (StringUtils.isBlank(phone) || phone.length() != 11) {
            throw new BusinessException(ResponseCodeEnum.ERROR, "请输入正确的手机号");
        }
        Admin dbInfo = this.getByPhone(phone);
        if (dbInfo != null) {
            throw new BusinessException(ResponseCodeEnum.ERROR, "此手机号已注册");
        }
    }

    private void verifyAccount(String account){
        if (!account.matches(RegularConst.LETTER_NUMBER_UNDERLINE2)) {
            throw new BusinessException(ResponseCodeEnum.ERROR, "账号必须由大小写字母和数字组成，不能有特殊符号");
        }
    }

    private void verifyPwd(String password) {
        // if (password == null || password.length() < 6) {
        //     throw new BusinessException(ResponseCodeEnum.ERROR, "密码长度至少6个字符");
        // }

        if (!password.matches(RegularConst.LETTER_NUMBER_UNDERLINE2)) {
            throw new BusinessException(ResponseCodeEnum.ERROR, "密码必须由大小写字母和数字组成，下划线可选");
        }
    }

    private String passwordGenerator(String password, String salt) {
        String str = SecureUtil.md5(password);
        str += salt;
        return Base64.encode(str);
    }

    @Override
    public String getCacheKey(String key) {
        return RedisMgtKeys.SESSIONID_KEY(key);
    }

    @Override
    public Admin getFromCache(String key) {
        // 先从缓存读取
        // this.caffeineCache.getIfPresent(key);
        return this.redisService.getCacheObject(this.getCacheKey(key));
        // return (Admin) this.caffeineCache.asMap().get(this.getCacheKey(key));
    }

    @Override
    public void setToCache(Admin admin) {
        String cacheKey = this.getCacheKey(admin.getToken());
        this.redisService.setCacheObject(cacheKey, admin);
        this.redisService.expire(admin.getToken(), SystemConfig.timeoutMgt, TimeUnit.MINUTES);
        // this.caffeineCache.put(cacheKey, admin);
    }

    @Override
    public void deleteForCache(String key) {
        this.redisService.deleteObject(this.getCacheKey(key));
        // this.caffeineCache.asMap().remove(this.getCacheKey(key));
    }

    @Override
    public void addFirst(Admin param) {
        int count = this.adminMapper.selectCount(null);
        if (count != 0) {
            throw new BusinessException(ResponseCodeEnum.ERROR, "第一个管理员已经存在");
        }
        this.verifyPwd(param.getPwd());
        Admin addInfo = this.getCreateInstance(Admin.class);
        // addInfo.setPhone(param.getPhone());
        addInfo.setAccount(param.getAccount());
        addInfo.setSalt(this.uuid());
        addInfo.setPwd(this.passwordGenerator(param.getPwd(), addInfo.getSalt()));
        addInfo.setUname(param.getUname());
        addInfo.setAvatar(param.getAvatar());
        this.adminMapper.insertSelective(addInfo);
    }

    @Override
    @OneClick
    @Transactional
    public void add(Admin param, SysAdmin admin) {
        this.getByAccount(param.getAccount());
        this.verifyPwd(param.getPwd());
        Admin addInfo = this.getCreateInstance(Admin.class, admin);
        addInfo.setPhone(param.getPhone());
        addInfo.setAccount(param.getAccount());
        addInfo.setSalt(this.uuid());
        addInfo.setPwd(this.passwordGenerator(param.getPwd(), addInfo.getSalt()));
        addInfo.setUname(param.getUname());
        addInfo.setAvatar(param.getAvatar());
        this.adminMapper.insertSelective(addInfo);
    }

    @Override
    public void del(String serviceID, SysAdmin admin) {
        Admin dbInfo = this.detail(serviceID, admin);
        if (dbInfo.getId().equals(admin.getSysAdminID())) {
            throw new BusinessException(ResponseCodeEnum.ERROR, "不能删除自己");
        }
        this.deleteLogic(dbInfo.getId(), admin, this.adminMapper);
    }

    @Override
    @OneClick
    @Transactional
    public void update(Admin param, SysAdmin admin) {
        Admin dbInfo = this.detail(param.getServiceID(), admin);
        if (!dbInfo.getPhone().equals(param.getPhone())) {
            this.verifyPhone(param.getPhone());
        }
        Admin storeInfo = this.getUpdateInstance(Admin.class, admin);
        storeInfo.setId(dbInfo.getId());
        storeInfo.setPhone(param.getPhone());
        storeInfo.setUname(param.getUname());
        storeInfo.setAvatar(param.getAvatar());

        this.adminMapper.updateByPrimaryKeySelective(storeInfo);
    }

    @Override
    public PageInfo<Admin> list(PageParam paramParam, SysAdmin admin) {
        PageHelper.startPage(paramParam.getPage(), paramParam.getPageSize());
        List<Admin> list = this.adminMapper.list(paramParam.getKv());
        return new PageInfo<>(list);
    }

    @Override
    public Admin detail(String serviceID, SysAdmin admin) {
        Admin dbInfo = this.getByServiceID(serviceID, Admin.class, this.adminMapper);
        Admin backInfo = new Admin();
        backInfo.setId(dbInfo.getId());
        backInfo.setServiceID(dbInfo.getServiceID());
        backInfo.setPhone(dbInfo.getPhone());
        backInfo.setUname(dbInfo.getUname());
        backInfo.setAvatar(dbInfo.getAvatar());
        return backInfo;
    }

    @Override
    public void resetPwd(Admin param, SysAdmin admin) {
        this.verifyPwd(param.getPwd());
        Admin dbInfo = this.detail(param.getServiceID(), admin);
        Admin storeInfo = this.getUpdateInstance(Admin.class, admin);
        storeInfo.setId(dbInfo.getId());
        storeInfo.setSalt(this.uuid());
        storeInfo.setPwd(this.passwordGenerator(param.getPwd(), storeInfo.getSalt()));
        this.adminMapper.updateByPrimaryKeySelective(storeInfo);
    }

    @Override
    public Admin login(Admin param) {
        Admin dbInfo = this.getByAccount(param.getAccount());
        if (dbInfo == null) {
            throw new BusinessException(ResponseCodeEnum.ERROR, "用户名和密码不匹配");
        }
        String paramPwd = this.passwordGenerator(param.getPwd(), dbInfo.getSalt());
        String dbPwd = dbInfo.getPwd();
        if (!paramPwd.equals(dbPwd)) {
            throw new BusinessException(ResponseCodeEnum.ERROR, "用户名和密码不匹配");
        }

        this.deleteForCache(dbInfo.getToken());
        String token = this.updateToken(dbInfo);
        Admin backInfo = new Admin();
        backInfo.setToken(token);
        backInfo.setPhone(dbInfo.getPhone());
        backInfo.setUname(dbInfo.getUname());
        backInfo.setAvatar(dbInfo.getAvatar());
        backInfo.setServiceID(dbInfo.getServiceID());
        return backInfo;
    }

    @Override
    public void logout(Admin admin) {
        this.deleteForCache(admin.getToken());
    }

    @Override
    public Admin getLoginAdmin(HttpServletRequest request) {
        String token = request.getHeader("token");
//        if (SystemConfig.debugMode) {
//            if (StringUtils.isBlank(token)) {
//                token = request.getHeader("mgt-token");
//            }
//        }
        if (StringUtils.isNotBlank(token)) {
            Admin cacheInfo = this.getFromCache(token);
            if (cacheInfo != null) {
                this.setToCache(cacheInfo);
                return cacheInfo;
            }
            throw new BusinessException(ResponseCodeEnum.MGT_NO_LOGIN);
        } else {
            throw new BusinessException(ResponseCodeEnum.MGT_NO_LOGIN);
        }
    }

}
